#include <iostream>
#include "VK_Window.h"
#include "VK_ValidationLayer.h"

VK_Window *VK_Window::getInstance()
{
    static VK_Window instance;
    return &instance;
}

VK_Window::~VK_Window()
{
    if (vulkanInstance && surface) {
        vkDestroySurfaceKHR(vulkanInstance, surface, nullptr);
    }

    if (vulkanInstance)
        validationLayer->cleanup(vulkanInstance);
    validationLayer->release();

    if (window)
        glfwDestroyWindow(window);
    glfwTerminate();
}

VkInstance VK_Window::createOrGetVulkanInstance()
{
    if (vulkanInstance)
        return vulkanInstance;

    validationLayer = new VK_ValidationLayer(DEBUG_MODE);

    if (!validationLayer->appendValidationLayerSupport()) {
        std::cerr << "validation layers requested, but not available!" << std::endl;
        return nullptr;
    }

    VkApplicationInfo appInfo;
    appInfo.pNext = nullptr;
    appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
    appInfo.pApplicationName = "GPU-Perf for Vulkan";
    appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
    appInfo.pEngineName = "Vulkan Framework";
    appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
    appInfo.apiVersion = VK_API_VERSION_1_0;

    VkInstanceCreateInfo createInfo{};
    createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
    createInfo.pApplicationInfo = &appInfo;

    auto extensions = VK_Window::getInstance()->getVulkanRequiredExtensions();
    createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
    createInfo.ppEnabledExtensionNames = extensions.data();

    validationLayer->adjustVkInstanceCreateInfo(createInfo);

    if (vkCreateInstance(&createInfo, nullptr, &vulkanInstance) != VK_SUCCESS) {
        std::cerr << "failed to create instance!" << std::endl;
        return nullptr;
    }

    validationLayer->setupDebugMessenger(vulkanInstance);

    return vulkanInstance;
}

VK_ValidationLayer *VK_Window::getValidationLayer() const
{
    return validationLayer;
}

GLFWwindow *VK_Window::createWindow(const std::string &title)
{
    if (!window) {
        window = glfwCreateWindow(width, height, title.data(), nullptr, nullptr);
    }

    if (window) {
        glfwSetWindowTitle(window, title.data());

        if (glfwCreateWindowSurface(vulkanInstance, window, nullptr, &surface) != VK_SUCCESS) {
            std::cout << "create vulkan surface failed" << std::endl;
            return nullptr;
        }
    }

    return window;
}

VkSurfaceKHR VK_Window::getSurface()
{
    return surface;
}

GLFWwindow *VK_Window::getWindow() const
{
    return window;
}

VK_Window::VK_Window()
{
    glfwInit();

    glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
    glfwWindowHint(GLFW_RESIZABLE, false);
}

std::vector<const char *> VK_Window::getVulkanRequiredExtensions()
{
    uint32_t glfwExtensionCount = 0;
    const char **glfwExtensions = nullptr;
    glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);

    std::vector<const char *> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);

    if (DEBUG_MODE)
        extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);

    return extensions;
}
